#include "Object3D.h"

dkm::Object3D::Object3D(const string xFileSrc, ID3DXEffect* mFX, const D3DXMATRIX* mView, const D3DXMATRIX* mProj, IDirect3DTexture9* mDefaultTex, const float x, const float y, const float z, const float rotateX, const float rotateY, const float rotateZ, const float scaleX, const float scaleY, const float scaleZ, const bool visible, const bool active)
	:Component(visible, active)
	,x(x)
	,y(y)
	,z(z)
	,rotationX(rotateX)
	,rotationY(rotateY)
	,rotationZ(rotateZ)
	,scaleX(scaleX)
	,scaleY(scaleY)
	,scaleZ(scaleZ)
	,mView(mView)
	,mProj(mProj)
	,mFX(mFX)
	,mDefaultTex(mDefaultTex)
	,id("")
	,hasPhysics(false)
	,hasCollider(false)
	,colShape(NULL)
	,thisBody(NULL)
	,thisMotionState(NULL)
	,trimesh(NULL)
	,collider(NULL)
{
	dkm::Engine::GetInstance()->AddComponent(this);
	GetHandles(mFX);
	LoadXFile(xFileSrc, &mMesh, mMtrl, mTex);
	D3DXMatrixIdentity(&mWorld);
}

dkm::Object3D::Object3D(const string xFileSrc, ID3DXEffect* mFX, const D3DXMATRIX* mView, const D3DXMATRIX* mProj, IDirect3DTexture9* mDefaultTex, const float x, const float y, const float z, const float rotateX, const float rotateY, const float rotateZ, const float scaleX, const float scaleY, const float scaleZ)
	:Component(true, true)
	,x(x)
	,y(y)
	,z(z)
	,rotationX(rotateX)
	,rotationY(rotateY)
	,rotationZ(rotateZ)
	,scaleX(scaleX)
	,scaleY(scaleY)
	,scaleZ(scaleZ)
	,mView(mView)
	,mProj(mProj)
	,mFX(mFX)
	,mDefaultTex(mDefaultTex)
	,id("")
	,hasPhysics(false)
	,hasCollider(false)
	,colShape(NULL)
	,thisBody(NULL)
	,thisMotionState(NULL)
	,trimesh(NULL)
	,collider(NULL)
{
	dkm::Engine::GetInstance()->AddComponent(this);
	GetHandles(mFX);
	LoadXFile(xFileSrc, &mMesh, mMtrl, mTex);
	D3DXMatrixIdentity(&mWorld);
}

dkm::Object3D::~Object3D()
{
	ReleaseCOM(mMesh);
	for(int i = 0; i < mTex.size(); ++i)
		ReleaseCOM(mTex[i]);

	if (NULL != colShape) delete colShape;
	colShape = NULL;
	if (NULL != thisBody) delete thisBody;
	thisBody = NULL;
	if (NULL != thisMotionState) delete thisMotionState;
	thisMotionState = NULL;
	if (NULL != trimesh) delete trimesh;
	trimesh = NULL;
	if (NULL != collider) delete collider;
	collider = NULL;	
}

D3DXVECTOR3* dkm::Object3D::Position()
{
	if (hasPhysics)
	{
		btTransform trans;
		thisMotionState->getWorldTransform(trans);
		return &(D3DXVECTOR3(trans.getOrigin()));
	}
	else
	{
		return &D3DXVECTOR3(x,y,z);
	}
}

btVector3* dkm::Object3D::BtVec3Position()
{
	if (hasPhysics)
	{
		btTransform trans;
		thisMotionState->getWorldTransform(trans);
		return &trans.getOrigin();
	}
	else
	{
		return &btVector3(x,y,z);
	}
}

void dkm::Object3D::GetHandles(ID3DXEffect* mFX)
{
	// Obtain handles.
	mhTech            = mFX->GetTechniqueByName("PhongDirLtTexTech");
	mhWVP             = mFX->GetParameterByName(0, "gWVP");
	mhWorldInvTrans   = mFX->GetParameterByName(0, "gWorldInvTrans");
	mhMtrl            = mFX->GetParameterByName(0, "gMtrl");
	mhWorld           = mFX->GetParameterByName(0, "gWorld");
	mhTex             = mFX->GetParameterByName(0, "gTex");
}

void dkm::Object3D::AddSphereCollider(const D3DXVECTOR3 &center, const float radius)
{
	hasCollider = true;
	delete collider; collider = NULL;
	collider = new Object3DCollider(center, radius);
}

void dkm::Object3D::AddBoxCollider(const D3DXVECTOR3 &center, const float length, const float height, const float width)
{
	hasCollider = true;
	delete collider; collider = NULL;
	collider = new Object3DCollider(center, length, height, width);
}

void dkm::Object3D::AddRigidBody(btDiscreteDynamicsWorld *physicsWorld, const std::string shape, const btVector3 &size, const btVector3 &center, const btScalar mass)
{
	if (!hasPhysics)
	{
		hasPhysics = true;
		if (shape == "box")
		{
			colShape = new btBoxShape(size);
		}
		else if (shape == "sphere")
		{
			colShape = new btSphereShape(size.x());
		}
		else if (shape == "capsule")
		{
			colShape = new btCapsuleShape(size.x(), size.y());
		}
		else if (shape == "mesh")
		{
			trimesh = new btTriangleMesh();
			CreateTriangleMeshFromMesh(mMesh, trimesh);
			btVector3 bMin, bMax;
			trimesh->calculateAabbBruteForce(bMin,bMax);
			//trimesh->setPremadeAabb(bMin,bMax);
			colShape = new btBvhTriangleMeshShape(trimesh,true);
		}
	
		thisTransform = btTransform();
		thisTransform.setIdentity();
		thisTransform.setOrigin(center);
		localInertia = btVector3(0,0,0);
		this->mass = mass;
		if (mass)
		{
			colShape->calculateLocalInertia(this->mass, localInertia);
		}
		thisMotionState = new btDefaultMotionState(thisTransform);
		btRigidBody::btRigidBodyConstructionInfo myBoxRigidBodyConstructionInfo( this->mass, thisMotionState, colShape, localInertia );
		thisBody = new btRigidBody(myBoxRigidBodyConstructionInfo);
		physicsWorld->addRigidBody(thisBody);
	}
}

void dkm::Object3D::CreateTriangleMeshFromMesh(ID3DXMesh *mesh, btTriangleMesh *trimesh)
{
	LPDIRECT3DVERTEXBUFFER9 meshVertices;
	mesh->GetVertexBuffer( &meshVertices );
	DWORD dwVertexSize = mesh->GetNumBytesPerVertex();
	VertexPNT* meshVertexData;
	meshVertices->Lock(0, 0, (void**)&meshVertexData, 0);

	LPDIRECT3DINDEXBUFFER9 meshIndices;
	mesh->GetIndexBuffer(&meshIndices);
	WORD* meshIndexData;
	meshIndices->Lock(0, 0, (void**)&meshIndexData, 0);

	for (int i = 0; i < mesh->GetNumFaces(); ++i)
	{
		float x1 = meshVertexData[meshIndexData[i*3]].pos.x;
		float y1 = meshVertexData[meshIndexData[i*3]].pos.y;
		float z1 = meshVertexData[meshIndexData[i*3]].pos.z;
		float x2 = meshVertexData[meshIndexData[i*3+1]].pos.x;
		float y2 = meshVertexData[meshIndexData[i*3+1]].pos.y;
		float z2 = meshVertexData[meshIndexData[i*3+1]].pos.z;
		float x3 = meshVertexData[meshIndexData[i*3+2]].pos.x;
		float y3 = meshVertexData[meshIndexData[i*3+2]].pos.y;
		float z3 = meshVertexData[meshIndexData[i*3+2]].pos.z;
		btVector3 pos1(x1,y1,z1);
		btVector3 pos2(x2,y2,z2);
		btVector3 pos3(x3,y3,z3);
		trimesh->addTriangle(pos1,pos2,pos3); 
	}

	meshIndices->Unlock();
	meshIndices->Release();
	meshVertices->Unlock();
	meshVertices->Release();
}

void dkm::Object3D::Draw()
{
	if (hasPhysics)
	{
		if (thisBody->getMotionState())
		{
			btDefaultMotionState* myMotionState = (btDefaultMotionState*)thisBody->getMotionState();
			myMotionState->m_graphicsWorldTrans.getOpenGLMatrix((btScalar*)&mWorld);
		} else
		{
			thisBody->getWorldTransform().getOpenGLMatrix((btScalar*)&mWorld);
		}

		D3DXMatrixScaling(&scale, scaleX, scaleY, scaleZ);
		mWorld = scale * mWorld;
	}
	else
	{
		D3DXMatrixTranslation(&pos, x, y, z);
		D3DXMatrixRotationYawPitchRoll(&rotation, rotationY, rotationX, rotationZ);
		D3DXMatrixScaling(&scale, scaleX, scaleY, scaleZ);
		mWorld = scale*rotation*pos;
	}
		
	//thisMotionState->m_graphicsWorldTrans.getOpenGLMatrix((btScalar*)&mWorld);

	HR(mFX->SetMatrix(mhWVP, &(mWorld*(*mView)*(*mProj))));
	D3DXMatrixInverse(&worldInvTrans, 0, &mWorld);
	D3DXMatrixTranspose(&worldInvTrans, &worldInvTrans);
	HR(mFX->SetMatrix(mhWorldInvTrans, &worldInvTrans));
	HR(mFX->SetMatrix(mhWorld, &mWorld));

	HR(mFX->SetTechnique(mhTech));
	UINT numPasses = 0;
	HR(mFX->Begin(&numPasses, 0));
	HR(mFX->BeginPass(0));

	for(int j = 0; j < mMtrl.size(); ++j)
	{
		HR(mFX->SetValue(mhMtrl, &mMtrl[j], sizeof(Mtrl)));
	
		// If there is a texture, then use.
		if(mTex[j] != 0)
		{
			HR(mFX->SetTexture(mhTex, mTex[j]));
		}

		// But if not, then set a pure white texture.  When the texture color
		// is multiplied by the color from lighting, it is like multiplying by
		// 1 and won't change the color from lighting.
		else
		{
			HR(mFX->SetTexture(mhTex, mDefaultTex));
		}
	
		HR(mFX->CommitChanges());
		HR(mMesh->DrawSubset(j));
	}
	HR(mFX->EndPass());
	HR(mFX->End());
}

void dkm::Object3D::PhysicsMove(const btVector3 &movement)
{
	btTransform trans = thisBody->getWorldTransform();
	trans.setOrigin(trans.getOrigin() + movement);
	thisBody->setWorldTransform(trans);

	thisMotionState->getWorldTransform(trans);
	trans.setOrigin(trans.getOrigin() + movement);		
	thisMotionState->setWorldTransform(trans);
}

void dkm::Object3D::PhysicsMove(const D3DXVECTOR3 &movement)
{
	btVector3 btMovement = btVector3(movement.x,movement.y,movement.z);
	btTransform trans = thisBody->getWorldTransform();
	trans.setOrigin(trans.getOrigin() + btMovement);
	thisBody->setWorldTransform(trans);

	thisMotionState->getWorldTransform(trans);
	trans.setOrigin(trans.getOrigin() + btMovement);		
	thisMotionState->setWorldTransform(trans);
}